#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/bitrev.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/blkdev.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/jiffies.h>
#include <linux/percpu.h>
#include <linux/uio.h>
#include <linux/idr.h>
#include <linux/bsg.h>
#include <linux/slab.h>

#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include <linux/miscdevice.h>
#include<linux/platform_device.h>

#define LED_NUM 1
#define LED_NAME "gpioLED"

struct gpioLED
{
    dev_t devid;
    int major;
    int minor;
    struct cdev led_cdev;
    struct class *class;
    struct device *device;
    struct device_node * node;
    int led_r_gpio;
};

static struct gpioLED led;

static int led_release (struct inode * node, struct file *file)
{
    return 0;
}
static int led_open (struct inode *node, struct file *file)
{
    return 0;
}

static ssize_t led_write (struct file *file, const char __user *buffer, size_t size, loff_t *loff)
{
    char flag=0;
    
    if(copy_from_user(&flag,buffer,1))
    {
        printk("copy_from_user error\r\n");
        return -1;
    }
    
    gpio_set_value(led.led_r_gpio,flag);
    
    return 0;
}
static const struct file_operations led_fop={
    .write=led_write,
    .open = led_open,
    .release=led_release,
    .owner = THIS_MODULE,
};


static int led_probe(struct platform_device *devptr)
{
    int ret = 0;
 
    led.node = devptr->dev.of_node;
    if(IS_ERR(led.node))
    {
        printk("not find node\r\n");
        goto probe_error;
    }
    led.led_r_gpio = of_get_named_gpio(led.node,"led-gpio-r",0);
    if(led.led_r_gpio<0)
    {
        printk("not find gpio\r\n");
        goto probe_error;
    }
    printk("gpio ID:%d\r\n",led.led_r_gpio);
    
    ret = gpio_request(led.led_r_gpio,"led_r_gpio");
    if(ret<0)
    {
        printk("request error!\r\n");
        goto probe_error;
    }
    ret = gpio_direction_output(led.led_r_gpio,1);
     if(ret<0)
    {
        printk("set direction error!\r\n");
        goto free_gpio_request;
    }
    
    return 0;

free_gpio_request:
    gpio_free(led.led_r_gpio);
probe_error:
    return -1;

}
static int  led_remove(struct platform_device *devptr)
{
    gpio_set_value(led.led_r_gpio,1);
    gpio_free(led.led_r_gpio);
    return 0;
}

static const struct of_device_id led_driver_ids[] = {
    { .compatible = "xiao,LED" },
    {                                           },
};
struct platform_driver  led_plat={
    .driver={
        .name = LED_NAME,
        .owner = THIS_MODULE,
        .of_match_table = led_driver_ids,
    },
    .probe= led_probe,
    .remove= led_remove,
};

struct miscdevice mis={
    .name = LED_NAME,
    .fops=&led_fop,
    .minor = 144,
};

static int __init gpioLED_init(void)
{
    int ret = 0;
    ret = misc_register(&mis);
    if(ret!=0)
        return -1;
    return platform_driver_register(&led_plat);
}
static void __exit gpioLED_exit(void)
{
    platform_driver_unregister(&led_plat);
    misc_deregister(&mis);
}
module_init(gpioLED_init);
module_exit(gpioLED_exit);
MODULE_ALIAS("xiao");
MODULE_LICENSE("GPL");
